<template>
  <div style="height:100%">
    <vxe-table v-loading="loading" show-header-overflow auto-resize resizable :stripe="isTree?false:true"
      :tree-config="isTree?{}:null" :edit-rules="mode>0?editRules:null" :edit-config="editConf[mode]"
      :span-method="spanMethod" :keep-source="mode>0" :sort-config="sortConfig" ref="ycTable" :data="tableData"
      v-on="listeners" v-bind="$attrs">
      <template v-slot:editOperation="{ row }">
        <yc-button v-if="isActiveByRow(row)" type="text" size="mini" label="保存" @click="hadnleSaveRow(row)" />
        <yc-button v-else type="text" size="mini" label="编辑" @click="setActiveRow(row)" />
        <yc-button v-if="isActiveByRow(row)" type="text" size="mini" label="取消" @click="hadnleCancelRow(row)" />
        <yc-button v-else size="mini" type="text" label="删除" @click="handleDeleteRow(row)" />
      </template>
      <template v-slot:deleteOperation="{ row }">
        <yc-button size="mini" type="text" label="删除" @click="handleDeleteRow(row)" />
      </template>
    </vxe-table>
    <yc-table-columns-filter ref="columnFilter" v-model="columnsFilter.value" :options="columnsFilter.options"
      @change="onColumnFilterChange" />
  </div>
</template>

<script>
import XEUtils from 'xe-utils'
import _ from 'lodash'
import util from '@/libs/util'
export default {
  name: 'yc-table',
  componentName: 'yc-table',
  props: {
    api: {
      type: Object,
      required: true,
      default: () => {
        return {}
      }
    },
    columns: {
      type: Array,
      default: () => {
        return []
      }
    },
    paging: {
      type: Boolean,
      default: true
    },
    primaryKey: {
      type: String,
      default: 'id'
    },
    editRules: {
      type: Object,
      default: () => {
        return {}
      }
    },
    spanMethod: {
      type: Function
    },
    sortConfig: {
      type: Object,
      default: () => {
        return {}
      }
    },
    pagination: {
      type: Object,
      default: () => {
        return {
          current: 1,
          size: 25,
          total: 1
        }
      }
    },
    mode: {
      type: Number,
      default: 0 // 0->close;1->cell;2->row
    },
    isTree: {
      type: Boolean,
      default: false
    },
    customQueryIndex: {
      type: Function
    },
    showLoading: {
      type: Boolean,
      default: true
    }
  },
  watch: {
    columns (value) {
      this.$nextTick(() => this.loadColumn(value))
    }
  },
  computed: {
    listeners () {
      return {
        ...this.$listeners
      }
    },
    table () {
      return this.$refs.ycTable
    }
  },
  data () {
    return {
      tableData: [],
      tableColumns: [],
      loading: true,
      // 主体表格列过滤
      columnsFilter: {
        value: [],
        options: []
      },
      editConf: [
        null,
        { trigger: 'click', mode: 'cell', showStatus: true, icon: 'fa fa-pencil-square-o' },
        { trigger: 'manual', mode: 'row', showStatus: false, icon: 'fa fa-file-text-o' }
      ]
    }
  },
  created () {
    this.loading = this.showLoading
    this.$on('validate', this.onValidate)
    this.initTableColumns()
    this.fetchTableData()
  },
  mounted () {
    if (this.columnsFilter.value && this.columnsFilter.value.length) {
      this.loadColumn(this.columnsFilter.value)
    }
  },
  methods: {
    loadColumn (columns) {
      this.tableColumns = XEUtils.clone(columns.filter(e => e.show !== false))
      const { $scopedSlots } = this.table
      Object.assign($scopedSlots, this.$scopedSlots)
      XEUtils.eachTree(this.tableColumns, column => {
        if (column.slots) {
          XEUtils.each(column.slots, (func, name, slots) => {
            if (!XEUtils.isFunction(func)) {
              if ($scopedSlots[func]) {
                slots[name] = $scopedSlots[func]
              } else {
                slots[name] = null
                console.error('vxe.error.notSlot', [func])
              }
            }
          })
        }
      })
      this.table.loadColumn(this.tableColumns)
    },
    reloadColumn (columns) {
      this.table.clearAll()
      return this.loadColumn(columns)
    },
    refresh (search) {
      this.fetchTableData(search)
    },
    fetchTableData (search) {
      if (this.showLoading) {
        this.loading = true
      }
      let param = this.paging ? {
        'pageNum': this.pagination.current,
        'pageSize': this.pagination.size
      } : {}
      Object.assign(param, search)
      if (this.api.index && !this.customQueryIndex) {
        this.api.index(param).then(res => {
          var { code, data } = res
          if (code === 200) {
            this.initTableData(data)
          } else {
            this.$message.error('查询失败')
          }
          if (this.showLoading) {
            this.loading = false
          }
        })
      }
      if (this.customQueryIndex) {
        this.customQueryIndex(param).then(res => {
          var { code, data } = res
          if (code === 200) {
            this.initTableData(data)
          } else {
            this.$message.error('查询失败')
          }
          if (this.showLoading) {
            this.loading = false
          }
        })
      }
    },
    initTableData (data) {
      let items = []
      if (data === null) {
        this.tableData = []
        return
      }
      if (this.paging) {
        this.pagination.current = data.pageNum
        this.pagination.total = data.total
        items = data.list
      } else {
        this.pagination.current = 1
        this.pagination.total = data.length
        items = data
      }
      this.tableData = this.isTree ? util.treeData(items) : items
    },
    initTableColumns () {
      this.columns.forEach(item => {
        if (item.type === 'checkbox' && this.isTree) {
          item.treeNode = true
        }
        if (item.show === undefined) {
          item.show = true
        }
      })
      this.columnsFilter.options = _.cloneDeep(this.columns)
      this.columnsFilter.value = _.cloneDeep(this.columns)
    },
    handleDeleteRow (row) {
      if (this.isTree && row.children && row.children.length) {
        this.$message.error('有子级不能删除')
        return
      }
      if (this.api.del) {
        var key = this.primaryKey
        var ids = []
        ids.push(row[key])
        this.$YConfirm('删除').then(() => {
          this.api.del(ids).then(res => {
            var { code } = res
            if (code === 200) {
              this.$message.success('删除成功')
              this.deleteRow(row)
            } else {
              this.$message.error('删除失败')
            }
          })
        })
      }
      this.$emit('on-delete', row)
    },
    hadnleSaveRow (row) {
      this.validate()
      this.clearActived(row).then(res => {
        var primaryKey = row[this.primaryKey]
        if (primaryKey) {
          if (this.api.modify) {
            this.api.modify(primaryKey, row).then(res => {
              var { code, data } = res
              if (code === 200) {
                Object.assign(row, data)
                this.$message.success('保存成功')
              } else {
                this.$message.error('保存失败')
              }
            })
          }
        } else {
          if (this.api.insert) {
            this.api.insert(row).then(res => {
              var { code, data } = res
              if (code === 200) {
                Object.assign(row, data)
                this.$message.success('新增成功')
              } else {
                this.$message.error('新增失败')
              }
            })
          }
        }
      })
    },
    hadnleCancelRow (row) {
      if (this.isInsertByRow(row)) {
        this.table.remove(row)
      } else {
        this.clearActived(row).then(() => {
          this.revertData(row)
        })
      }
    },
    async insertRow (record, activeField, row) {
      if (this.isTree) {
        this.insertTreeRow(record, activeField)
        return
      }
      let { row: newRow } = await this.table.insertAt(record, row)
      if (activeField) {
        await this.table.setActiveCell(newRow, activeField)
      }
    },
    insertTreeRow (record, activeField) {
      if (this.isTree && record.parentId > 0) {
        var { item } = XEUtils.findTree(this.tableData, e => e[this.primaryKey] === record.parentId)
        if (item) {
          if (item.children === undefined) {
            item.children = []
          }
          item.children.push(record)
        }
      } else {
        this.tableData.splice(0, 0, record)
      }
      this.syncData()
    },
    updateRow (oldRow, newRow) {
      if (this.isTree) {
        this.deleteRow(oldRow)
        this.insertTreeRow(newRow)
      } else {
        Object.assign(oldRow, newRow)
      }
    },
    deleteRow (row) {
      var key = [this.primaryKey]
      if (this.isTree) {
        if (row.parentId === 0) {
          var index = this.tableData.findIndex(e => e[key] === row[key])
          if (index > -1) { this.tableData.splice(index, 1) }
        } else {
          var { item } = XEUtils.findTree(this.tableData, e => e[key] === row.parentId)
          var childIndex = item.children.findIndex(e => e[key] === row[key])
          if (childIndex > -1) { item.children.splice(childIndex, 1) }
        }
      } else {
        this.table.remove(row)
      }
      this.syncData()
    },
    async validate () {
      const errMap = await this.table.validate(true).catch(errMap => errMap)
      return errMap
    },
    async onValidate (result) {
      const errMap = await this.validate()
      if (errMap) {
        result.valid = true
      } else {
        result.valid = false
      }
    },
    syncData () {
      this.table.syncData()
    },
    getTableData () {
      return this.table.getTableData().fullData
    },
    setActiveRow (row) {
      this.table.setActiveRow(row)
    },
    isActiveByRow (row) {
      return this.table.isActiveByRow(row)
    },
    clearActived (row) {
      return this.table.clearActived(row)
    },
    clearAllActived () {
      return this.table.clearActived()
    },
    revertData (row) {
      return this.table.revertData(row)
    },
    isInsertByRow (row) {
      return this.table.isInsertByRow(row)
    },
    toggleRowExpand (row) {
      return this.table.toggleRowExpand(row)
    },
    isExpandByRow (row) {
      return this.table.isExpandByRow(row)
    },
    isRowExpandLoaded (row) {
      return this.table.isRowExpandLoaded(row)
    },
    setAllCheckboxRow (checked) {
      this.table.setAllCheckboxRow(checked)
    },
    toggleAllCheckboxRow () {
      this.table.toggleAllCheckboxRow()
    },
    clearCheckboxRow () {
      this.table.clearCheckboxRow()
    },
    getCurrentRecord () {
      return this.table.getCurrentRecord()
    },
    getCheckboxRecords () {
      return this.table.getCheckboxRecords()
    },
    invertSelectionRow () {
      this.tableData.forEach(row => {
        this.table.toggleCheckboxRow(row)
      })
    },
    openRowExpand (row) {
      if (this.isExpandByRow(row) === false) {
        return this.toggleRowExpand(row)
      }
      return Promise.resolve()
    },
    deleteSelectionRow () {
      var records = this.table.getCheckboxRecords()
      if (records.length <= 0) {
        this.$message('未选中记录')
        return
      }
      var ids = records.map(item => { return item[this.primaryKey] })
      this.$YConfirm('删除').then(() => {
        this.api.del(ids).then(res => {
          var { code } = res
          if (code === 200) {
            this.$message.success('删除成功')
            this.table.remove(records)
          } else {
            this.$message.error('删除失败')
          }
        })
      })
    },
    onColumnFilterChange (columns) {
      this.columnsFilter.value = columns
      this.reloadColumn(this.columnsFilter.value)
    },
    handleOpenSetting () {
      this.$refs.columnFilter.start()
    },
    getUpdateFields (keepField = []) {
      var table = this.table
      const { keepSource, tableFullData, treeConfig, treeOpts } = table
      // 在 v3 中必须要开启 keep-source
      if (!keepSource) {
        console.warn('vxe.error.reqProp', ['keep-source'])
      }
      if (keepSource) {
        let attr = []
        if (treeConfig) {
          XEUtils.eachTree(tableFullData, row => {
            var obj = this.isUpdateByRow(row, keepField)
            if (!XEUtils.isEmpty(obj)) {
              obj[this.primaryKey] = row[this.primaryKey]
              attr.push(obj)
            }
          }, treeOpts)
          return attr
        }

        tableFullData.forEach(row => {
          var obj = this.isUpdateByRow(row, keepField)
          if (!XEUtils.isEmpty(obj)) {
            obj[this.primaryKey] = row[this.primaryKey]
            attr.push(obj)
          }
        })
        return attr
      }
      return []
    },
    /**
   * 检查行或列数据是否发生改变
   * @param {Row} row 行对象
   * @param {String} keepField 字段名
   */
    isUpdateByRow (row, keepField = []) {
      var table = this.table
      let updateDataObj = {}
      const { visibleColumn, keepSource, treeConfig, treeOpts, tableSourceData, fullDataRowIdData } = table
      if (keepSource) {
        let oRow, property
        const rowid = table.getRowid(row)
        // 新增的数据不需要检测
        if (!fullDataRowIdData[rowid]) {
          return false
        }
        if (treeConfig) {
          const children = treeOpts.children
          const matchObj = XEUtils.findTree(tableSourceData, item => rowid === table.getRowid(item), treeOpts)
          row = Object.assign({}, row, { [children]: null })
          if (matchObj) {
            oRow = Object.assign({}, matchObj.item, { [children]: null })
          }
        } else {
          const oRowIndex = fullDataRowIdData[rowid].index
          oRow = tableSourceData[oRowIndex]
        }
        if (oRow) {
          for (let index = 0, len = visibleColumn.length; index < len; index++) {
            property = visibleColumn[index].property
            if (property && !this.eqCellValue(oRow, row, property)) {
              updateDataObj[property] = row[property]
            }
          }
          if (keepField.length && JSON.stringify(updateDataObj) !== '{}') {
            keepField.forEach(property => {
              updateDataObj[property] = row[property]
            })
          }
        }
      }
      return updateDataObj
    },
    eqCellValue (row1, row2, field) {
      const val1 = XEUtils.get(row1, field)
      const val2 = XEUtils.get(row2, field)
      if (this.eqCellNull(val1) && this.eqCellNull(val2)) {
        return true
      }
      if (XEUtils.isString(val1) || XEUtils.isNumber(val1)) {
        /* eslint-disable eqeqeq */
        return val1 == val2
      }
      return XEUtils.isEqual(val1, val2)
    },
    eqCellNull (cellValue) {
      return cellValue === '' || XEUtils.eqNull(cellValue)
    }
  }
}
</script>
